﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Meta.Numerics.Statistics.Distributions;

namespace ExcelAddIn1
{
    /**
     * Implementation of a one-way ANOVA.
     */
    class Test_ANOVA : Test
    {
        public Test_ANOVA(DataContainer data)
        {
            this.data = data;
            res = new List<String>();
        }

        public override string GetInfo()
        {
            String s = "Performs a One-way Analysis of Variance (ANOVA) to check for significant differences between three or more datasets. The test requires that the data is normally distributed, independent and have equal variances.";
            return s;
        }

        public override void RunTest()
        {
            res.Add("ANOVA");

            //Error check
            if (data.GetNoSets() < 3)
            {
                res.Add("At least three datasets is required!");
                return;
            }

            int k = data.GetNoSets();

            //Calculate total sum of squares and total n.
            double ntot = 0;
            double totSum = 0.0;
            double totSqSum = 0.0;
            for (int s = 0; s < k; s++)
            {
                DataSet d = data.GetDataSet(s);
                ntot += (double)d.GetN();
                totSum += d.GetSum();
                totSqSum += d.GetSquaredSum();
            }
            double SST = totSqSum - Math.Pow(totSum, 2) / ntot;

            //Calculate grand mean
            double grandMean = 0.0;
            for (int s = 0; s < k; s++)
            {
                DataSet d = data.GetDataSet(s);
                grandMean += d.GetMean() * (double)d.GetN();
            }
            grandMean = grandMean / ntot;

            //Calculate SSB and SSW
            double SSB = 0.0;
            for (int s = 0; s < k; s++)
            {
                DataSet d = data.GetDataSet(s);
                SSB += (double)d.GetN() * Math.Pow(d.GetMean() - grandMean, 2);
            }
            double SSW = SST - SSB;

            //Calculate F-score
            double MSB = SSB / (double)(k - 1);
            double MSW = SSW / (ntot - (double)k);
            double F = MSB / MSW;

            //Get Critical F
            double DF1 = (double)(k - 1);
            double DF2 = ntot - (double)k;

            FisherDistribution fdist = new FisherDistribution(DF1, DF2);
            double Fc = fdist.InverseRightProbability(alpha);
            double total = SSB + SSW;
            double totalDF = DF1 + DF2;

            double p = fdist.RightProbability(F);

            //Results
            res.Add("Source of variaton;SS;DoF;MS;F;-");
            res.Add("Between samples;" + SSB.ToString("F2") + ";" + (int)DF1 + ";" + MSB.ToString("F2") + ";" + F.ToString("F2"));
            res.Add("Within samples;" + SSW.ToString("F2") + ";" + (int)DF2 + ";" + MSW.ToString("F2"));
            res.Add("Total;" + total.ToString("F2") + ";" + (int)totalDF);
            res.Add("α;" + alpha.ToString("F2") + ";;;;-");
            res.Add("P-value;" + p.ToString("F3"));
            res.Add("F-score;" + F.ToString("F2"));
            res.Add("F-crit;" + Fc.ToString("F2"));
            if (F > Fc)
            {
                res.Add("Result;Significant difference at level " + p.ToString("F3"));
            }
            else
            {
                res.Add("Result;No difference (significance level " + p.ToString("F3") + ")");
            }
            res.Add(";;;;;-");

            //Scheffe's Pairwise Comparison post-test
            double Fsc = Fc * (double)(k - 1);
            res.Add("Scheffe's Pairwise Comparison");
            res.Add("Fsc;" + Fsc.ToString("F2") + ";;-");

            for (int i = 0; i < k; i++)
            {
                for (int j = 0; j < k; j++)
                {
                    if (i != j && i < j)
                    {
                        DataSet d1 = data.GetDataSet(i);
                        DataSet d2 = data.GetDataSet(j);
                        double Fs = Math.Pow(d1.GetMean() - d2.GetMean(), 2) / (MSW * (1.0 / (double)d1.GetN() + 1.0 / (double)d2.GetN()));

                        if (Fs > Fsc)
                        {
                            res.Add("Fs(" + (i + 1) + "," + (j + 1) + ");" + Fs.ToString("F2") + ";Difference");
                        }
                        else
                        {
                            res.Add("Fs(" + (i + 1) + "," + (j + 1) + ");" + Fs.ToString("F2") + ";No difference");
                        }
                    }
                }
            }
            res.Add(";;;-");
        }
    }
}
